/**
 * 
 */
package com.fnic.pearl.scheduler.mod;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import com.fnic.pearl.scheduler.SchedulerConfig;
import com.fnic.pearl.scheduler.constant.CondHeaderField;
import com.fnic.pearl.scheduler.constant.SchedulerUrl;
import com.fnic.pearl.scheduler.model.BatchLogin;
import com.fnic.pearl.scheduler.model.ProbeStatus;
import com.fnic.pearl.scheduler.model.SchedulerVersion;
import com.fnic.pearl.scheduler.util.ResponseUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * 负责鉴权管理相关，如：登录 管理节点，校验 probe
 * 
 * @author HuHaiyang
 * @date 2013年9月25日
 */
public class AuthManager {
	// (LIMIT+1)次与控制器之间心跳失败，才算是失联，避免网络偶发原因，单线程情况下不需要考虑原子性问题
	private static volatile int LIMIT = 3;
	private static AuthManager instance = new AuthManager();
	private static final Logger LOG = Logger.getLogger(AuthManager.class);

	public static AuthManager getInstance() {
		return instance;
	}

	private volatile boolean isLoginManager = false;
	private String managerSessionId;
	private static final String batch_url = new StringBuffer().append("http://")
			.append(SchedulerConfig.getInstance().getManagerIp()).append(":")
			.append(SchedulerConfig.getInstance().getManagerPort()).append(SchedulerUrl.PROBE_BATCH_LOGIN).toString();
	private Gson gson = new GsonBuilder().create();

	/**
	 * 登录至管理节点
	 * 
	 * @return true 登录成功
	 * @return false 登录失败
	 */
	public synchronized boolean loginManager(String ip, short port) {
		if (isLoginManager) {
			LOG.debug("already login to manager - " + ip + ":" + port);
			return true;
		}
		// 上报所有在线的 probe
		// for (ProbeStatus ps : lstProbes) {
		// // if (authProbe(ps, true) == null) {
		// // ProbeStatusManager.getInstance().updateStatus(ps.getProbeId(),
		// // ProbeStatus.S_LOGOUT);
		// // }
		// authProbe(ps, false);
		// }
		// 批量登录
		return login(ip, port);
	}

	/**
	 * 登录请求
	 * 
	 *
	 */
	private boolean login(String ip, short port) {
		final StringBuffer loginUrl = new StringBuffer(500);
		loginUrl.append("http://").append(ip).append(":").append(port).append(SchedulerUrl.SCHEDULER_LOGIN).append("?")
				.append(CondHeaderField.COND_SCHEDULER_ID).append("=")
				.append(SchedulerConfig.getInstance().getSchedulerId()).append("&")
				.append(CondHeaderField.COND_SCHEDULER_VER).append("=").append(SchedulerVersion.version).append("&")
				.append(CondHeaderField.SCHEDULER_IP).append("=")
				.append(SchedulerConfig.getInstance().getInfoSchedulerIp()).append("&")
				.append(CondHeaderField.SCHEDULER_PORT).append("=")
				.append(SchedulerConfig.getInstance().getInfoSchedulerPort()).append("&")
				.append(CondHeaderField.SCHEDULER_INNER_IP).append("=")
				.append(SchedulerConfig.getInstance().getInfoSchedulerInnerIp()).append("&")
				.append(CondHeaderField.SCHEDULER_INNER_PORT).append("=")
				.append(SchedulerConfig.getInstance().getInfoSchedulerInnerPort()).append("&")
				.append(CondHeaderField.SCHEDULER_DOMAIN).append("=")
				.append(SchedulerConfig.getInstance().getSchedulerDomain());
		LOG.info("start login to manager - " + loginUrl);
		HttpGet httpGet = new HttpGet(loginUrl.toString());
		DefaultHttpClient client = new DefaultHttpClient();
		// InputStreamReader inputReader = null;
		// BufferedReader reader = null;
		// InputStream instream = null;
		String str = "";
		try {
			// 设置超时
			HttpParams params = client.getParams();
			HttpConnectionParams.setConnectionTimeout(params, (int) 10 * 1000);
			HttpConnectionParams.setSoTimeout(params, 10 * 1000);
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			// if (entity != null) {
			// instream = entity.getContent();
			// inputReader = new InputStreamReader(instream);
			// reader = new BufferedReader(inputReader);
			// String str;
			// while ((str = reader.readLine()) != null) {
			// sb.append(str);
			// }
			// }
			if (entity != null) {
				str = EntityUtils.toString(entity);
			}
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpServletResponse.SC_OK) {
				LOG.info("login failed, status:" + statusCode);
				return false;
			}
			LOG.info("Allocate scheduleId - " + str);
			int id = SchedulerConfig.getInstance().getSchedulerId();
			// 如果返回的id不一致则更新配置文件，更新配置文件
			if (!StringUtils.equals(String.valueOf(id), str) && StringUtils.isNotBlank(str)) {
				SchedulerConfig.getInstance().writeProperty(SchedulerConfig.KEY_SCHEDULER_ID, str);
				SchedulerConfig.getInstance().setSchedulerId(Integer.valueOf(str));
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return false;
		} finally {
			// if (null != reader) {
			// reader.close();
			// }
			// if (null != inputReader) {
			// inputReader.close();
			// }
			// if (null != instream) {
			// instream.close();
			// }
			client.getConnectionManager().shutdown();
		}
		// 保存 sessionId
		List<Cookie> cookies = client.getCookieStore().getCookies();
		if (cookies.isEmpty()) {
			// 临时分配一个 sessionId
			cookies.add(new BasicClientCookie(CondHeaderField.SESSION_ID, UUID.randomUUID().toString()));
			// LOG.warn("cookie is empty!");
			// return false;
		}

		for (Cookie c : cookies) {
			if (c.getName().equals(CondHeaderField.SESSION_ID)) {
				this.managerSessionId = c.getValue();
				break;
			}
		}

		if (this.managerSessionId == null) {
			LOG.warn("not set session id cookie!");
			return false;
		}
		isLoginManager = true;
		LOG.info("login manager succ - session:" + this.managerSessionId);
		batchLogin();
		return true;
	}

	/**
	 * 批量登录
	 * 
	 * @param lstProbes
	 *
	 */
	private void batchLogin() {
		List<ProbeStatus> lstProbes = ProbeStatusManager.getInstance().getAllLogin();
		if (lstProbes == null || lstProbes.size() == 0) {
			return;
		}
		List<BatchLogin> batch = new ArrayList<>();
		Iterator<ProbeStatus> it = lstProbes.iterator();
		while (it.hasNext()) {
			ProbeStatus ps = it.next();
			batch.add(new BatchLogin(ps));
		}
		String batchStr = gson.toJson(batch);
		LOG.info("探针批量login开始|" + batchStr);
		ResponseUtil.sendPostData(batch_url, batchStr);
	}

	/**
	 * 获取是否已登录 manager
	 */
	public synchronized void isLoginManager(boolean logined) {
		isLoginManager = logined;
	}

	public boolean isLoginManager() {
		return isLoginManager;
	}

	/**
	 * 获取与 manager 之间的 sessionId
	 */
	public synchronized String getSessionIdWithManager() {
		return this.managerSessionId;
	}

	/**
	 * 至 管理节点 校验探针登录信息
	 * 
	 * @param needNotifyProbe
	 *            是否需要(通过心跳)通知 probe，对于 探针登录 是不需要的
	 * 
	 *            校验成功返回 true，否则返回 false
	 */
	// public SessionKey authProbe(ProbeStatus ps, boolean needNotifyProbe) {
	// if (ps == null) {
	// return null;
	// }
	// LOG.info("向msgdriver校验probe信息|" + ps.getProbeId());
	// StringBuffer url = new
	// StringBuffer().append("http://").append(SchedulerConfig.getInstance().getManagerIp())
	// .append(":").append(SchedulerConfig.getInstance().getManagerPort()).append(SchedulerUrl.PROBE_LOGIN)
	// .append("?").append(CondHeaderField.COND_PROBE_ID).append("=").append(ps.getProbeId()).append("&")
	// .append(CondHeaderField.COND_SCHEDULER_ID).append("=")
	// .append(SchedulerConfig.getInstance().getSchedulerId());
	// if (ps.getIcareType() > 0) {
	// url.append("&").append(CondHeaderField.COND_PROBE_ICARETYPE).append("=").append(ps.getIcareType());
	// }
	//
	// if (ps.getMac() != null) {
	// String enMac = null;
	// try {
	// enMac = URLEncoder.encode(ps.getMac(), "utf-8");
	// } catch (UnsupportedEncodingException e) {
	// LOG.warn("can not encode [mac] of " + ps + " | exception: " +
	// e.getMessage());
	// return null;
	// }
	// url.append("&").append(CondHeaderField.COND_PROBE_MAC).append("=").append(enMac);
	// }
	//
	// if (ps.getLastLoginIp() != null) {
	// url.append("&").append(CondHeaderField.COND_PROBE_IP).append("=").append(ps.getLastLoginIp());
	// }
	//
	// if (ps.getProbeSerial() != null) {
	// url.append("&").append(CondHeaderField.COND_PROBE_SERIAL).append("=").append(ps.getProbeSerial());
	// }
	// // LOG.info("probeLogin请求msgdriver：" + url.toString());
	// HttpPost statusRequest = new HttpPost(url.toString());
	// HttpResponse response =
	// ActiveToManager.getInstance().sendRequest(statusRequest);
	// SessionKey key = null;
	// try {
	// if (response == null) {
	// return null;
	// }
	// int statusCode = response.getStatusLine().getStatusCode();
	// if (statusCode != HttpServletResponse.SC_OK) {
	// return null;
	// }
	// HttpEntity entity = response.getEntity();
	//
	// if (entity != null) {
	// try {
	// String json = EntityUtils.toString(entity);
	// LOG.info("JSON - " + json);
	// key = gson.fromJson(json, SessionKey.class);
	// // LOG.info("auth probe - " + key);
	// if (key != null) {
	// key.setCreateTime(new Date());
	// if (needNotifyProbe) {
	// ProbeSessionKeyManager.getInstance().putWissue(ps.getProbeId(), key);
	// }
	// }
	// } catch (JsonSyntaxException jse) {
	// LOG.warn("SessionKey json syntax parse: " + jse.getMessage());
	// } catch (ParseException e) {
	// LOG.warn("Parse entity exception: " + e.getMessage());
	// } catch (IOException e) {
	// LOG.warn("Read entity exception: " + e.getMessage());
	// }
	// }
	// } catch (Exception e) {
	// LOG.error(e.getMessage(), e);
	// } finally {
	// statusRequest.releaseConnection();
	// }
	// return key;
	// }

	/**
	 * 计数器减
	 * 
	 *
	 */
	public void countDown() {
		if (LIMIT - 1 <= 0) {
			// 重新赋值
			restore();
			// 并且设置成未登录状态
			isLoginManager = false;
		} else {
			LIMIT--;
		}
	}

	/**
	 * 恢复心跳
	 * 
	 *
	 */
	public void restore() {
		LIMIT = 3;
	}

	/**
	 * 获得剩余重连次数
	 * 
	 * @return
	 *
	 */
	public int getLimit() {
		return LIMIT;
	}

	/**
	 * 管理节点已被重启，将进行本地的探针上报
	 * 
	 *
	 */
	public void controllRestart(String ip, short port) {
		if (isLoginManager) {
			LOG.info("此调度器尚未登录，或者管理节点已被重启!即将进行probe批量Login");
			login(ip, port);
		}
	}
}
